package org.easy.excel.support.parse;

import cn.hutool.core.util.StrUtil;
import org.easy.excel.support.annotations.ExportLabel;
import org.easy.excel.support.annotations.ExportTopLabel;
import org.easy.excel.support.parse.source.ExcelFormatSource;
import org.easy.excel.support.parse.source.ExcelTopLabelSource;
import org.easy.excel.support.parse.source.ExportLabelSource;
import org.tool.core.lang.date.DateFormat;
import org.tool.core.lang.date.DateUtil;
import org.tool.reflect.JavaBeanUtil;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static cn.hutool.core.text.CharSequenceUtil.EMPTY;
import static java.math.BigDecimal.ZERO;
import static org.easy.excel.support.parse.source.ExcelTopLabelSource.EMPTY_EXCEL_TOP_TITLE;

/**
 * {@link AnnotationExcelSourceParse} 接口的抽象实现类
 *
 * @author 李坤
 * @date 2022/6/8 23:03
 */
@SuppressWarnings("CollectionAddAllCanBeReplacedWithConstructor")
public abstract class AbstractAnnotationExcelSourceParse implements AnnotationExcelSourceParse {

    /**
     * SimpleDateFormat 对象与模板映射
     */
    private final Map<String, DateTimeFormatter> dateTimeFormatterCollection = new ConcurrentHashMap<>();

    /**
     * 实体类 到 Excel标题 映射容器
     */
    private final Map<Class<?>, List<List<String>>> class2ExcelTitleContainer = new ConcurrentHashMap<>();

    /**
     * 存储 Excel 导出的顶层标题
     */
    private final Map<Class<?>, ExcelTopLabelSource> excelTopLabelSourceCollection = new ConcurrentHashMap<>();

    /**
     * 存储 Excel 导出对应的实体类的解析结果
     */
    private final Map<Class<?>, List<ExportLabelSource>> exportLabelSourceCollection = new ConcurrentHashMap<>();



    /*--------------------------------------Implements method in ExcelSourceParse-------------------------------------*/


    @Override
    public List<List<String>> getSourceTitles(Object target) {
        if (target instanceof Class<?>) {
            List<List<String>> excelTitles;
            if (Objects.isNull(excelTitles = class2ExcelTitleContainer.get(target))) {
                excelTitles = getSourceTitles4Annotation((Class<?>) target);
            }
            final List<List<String>> result = new ArrayList<>();
            result.addAll(excelTitles);
            return result;
        }
        return Collections.emptyList();
    }

    @Override
    public List<List<String>> parseExportDataList(List<?> rawDataCollection) throws Exception {
        final Class<?> clazz = rawDataCollection.get(0).getClass();

        // 是否处理合计行
        final boolean appendTotalRow = appendTotalRow(clazz);
        // 类-字段 定义的元注解数据
        final List<ExportLabelSource> exportLabelSources = getAnnotationExcelSourceList(clazz);
        // 导出数据的总行数
        final int size = appendTotalRow ? rawDataCollection.size() + 1 : rawDataCollection.size();
        // Excel 最后的合计行结果集合
        final Map<String, BigDecimal> title2TotalRowMap = appendTotalRow ? new HashMap<>() : null;

        // 需要解析字段列的个数
        final int columnSize = exportLabelSources.size();
        // 解析后的导出数据
        final List<List<String>> parsedDataCollection = new ArrayList<>(size);

        List<String> innerList;
        // 遍历格式化处理集合中所有的数据
        for (int index = 0; index < size; index++) {
            parsedDataCollection.add(innerList = new ArrayList<>(columnSize));

            for (ExportLabelSource exportLabelSource : exportLabelSources) {
                // 如果该列数据为 序列号 ，则其值将为该行的行号
                if (Objects.equals(exportLabelSource.getField(), EMPTY)) {
                    if (appendTotalRow && index != size - 1) {
                        innerList.add(String.valueOf(index));
                    } else {
                        innerList.add("合计：");
                    }
                } else {
                    if (appendTotalRow && index != size - 1) {
                        innerList.add(parseGivenDate(rawDataCollection.get(index), exportLabelSource, title2TotalRowMap));
                    } else {
                        BigDecimal totalRow = title2TotalRowMap.get(exportLabelSource.getField());
                        innerList.add(Objects.isNull(totalRow) ? EMPTY : totalRow.toString());
                    }
                }
            }
        }
        return parsedDataCollection;
    }


    /*---------------------------------Implements method in AnnotationExcelSourceParse--------------------------------*/


    @Override
    public ExcelTopLabelSource getExcelTopTitle(Class<?> clazz) {
        ExcelTopLabelSource excelTopLabelSource;

        if (Objects.isNull(excelTopLabelSource = excelTopLabelSourceCollection.get(clazz))) {
            excelTopLabelSource = registerExcelTopTitle(clazz);
        }
        return excelTopLabelSource;
    }

    @Override
    public List<ExportLabelSource> getAnnotationExcelSourceList(Class<?> clazz) {
        List<ExportLabelSource> exportLabelSources;

        if (Objects.isNull(exportLabelSources = exportLabelSourceCollection.get(clazz))) {
            final ExcelTopLabelSource excelTopLabelSource = getExcelTopTitle(clazz);
            exportLabelSources = registerAnnotationExcelSource(clazz, excelTopLabelSource.isAppendSequence());
        }
        return exportLabelSources;
    }


    /*------------------------------------------------protected methods-----------------------------------------------*/


    /**
     * 获取给定实体类的标题数据
     *
     * @param clazz 给定的实体类
     *
     * @return 标题数据
     */
    protected List<List<String>> getSourceTitles4Annotation(Class<?> clazz) {
        List<ExportLabelSource> exportLabelSources;
        final ExcelTopLabelSource excelTopLabelSource = getExcelTopTitle(clazz);

        if (Objects.isNull(exportLabelSources = exportLabelSourceCollection.get(clazz))) {
            // 导出时是否添加序列号
            final boolean appendSequence = excelTopLabelSource.isAppendSequence();
            exportLabelSources = registerAnnotationExcelSource(clazz, appendSequence);
        }
        final List<List<String>> excelTitleList = exportLabelSources.stream()
                .map(ExportLabelSource::getTitles).collect(Collectors.toList());        // 此处有顺序要求，不能使用多线程处理

        // 获取当前实体类的标题，并将其存储起来
        final List<List<String>> excelTitles;
        class2ExcelTitleContainer.put(clazz, (excelTitles = mergedTopTitleAndTitleList(excelTopLabelSource, excelTitleList)));

        return excelTitles;
    }

    /**
     * 解析导出的数据，根据其给定的格式，将其转换为字符串
     *
     * @param source            源数据
     * @param exportLabelSource 字段注解
     * @param title2TotalRowMap 汇总数据
     *
     * @return 解析后的字符串
     */
    protected String parseGivenDate(Object source, ExportLabelSource exportLabelSource, Map<String, BigDecimal> title2TotalRowMap) {
        Object parseResult;
        final String field = exportLabelSource.getField();

        if (Objects.nonNull(parseResult = JavaBeanUtil.getBeanProperty(field, source))) {
            final Class<?> returnType = exportLabelSource.getReturnType();
            final ExcelFormatSource excelFormatSource = exportLabelSource.getExcelFormatSource();

            if (returnType.equals(BigDecimal.class)) {
                final BigDecimal decimalUnit = excelFormatSource.getDecimalUnit();
                parseResult = ((BigDecimal) parseResult).divide(decimalUnit, excelFormatSource.getDecimal(), excelFormatSource.getRoundingMode());

                // 计算该列数据的合计值
                if (Objects.nonNull(title2TotalRowMap)) {
                    BigDecimal decimal = title2TotalRowMap.get(field);
                    title2TotalRowMap.put(field, InnerUtil.securityIncrease(decimal, (BigDecimal) parseResult));
                }
            } else if (returnType.equals(Date.class)) {
                final LocalDateTime localDateTime = DateUtil.date2LocalDateTime((Date) parseResult);
                return localDateTime.format(getSimpleDateFormat(excelFormatSource));
            }
            return String.valueOf(parseResult);
        }
        return EMPTY;
    }

    /**
     * 获取并注册给定实体类的顶层标题
     *
     * @param clazz 给定实体类
     *
     * @return 给定实体类的顶层标题
     */
    protected ExcelTopLabelSource registerExcelTopTitle(Class<?> clazz) {
        final ExportTopLabel exportTopLabel;
        ExcelTopLabelSource excelTopLabelSource = EMPTY_EXCEL_TOP_TITLE;

        if (Objects.nonNull(exportTopLabel = clazz.getAnnotation(ExportTopLabel.class))) {
            excelTopLabelSource = ExcelTopLabelSource.getInstance(exportTopLabel);
            excelTopLabelSourceCollection.put(clazz, excelTopLabelSource);
        }
        return excelTopLabelSource;
    }

    /**
     * 向缓存中注册并获取给定类型解析后的 AnnotationExcelSource 数据
     *
     * @param clazz          给定类型
     * @param appendSequence 是否需要添加序号
     *
     * @return 给定类型解析后的 AnnotationExcelSource 数据
     */
    protected List<ExportLabelSource> registerAnnotationExcelSource(Class<?> clazz, boolean appendSequence) {
        int defaultTitleSize = -1;
        final List<ExportLabelSource> exportLabelSourceList = new ArrayList<>();

        // 解析给定的类型的所有字段，寻找其中的 @ExportLabel 注解
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            ExportLabelSource exportLabelSource = null;
            final ExportLabel exportLabel = field.getAnnotation(ExportLabel.class);

            if (Objects.nonNull(exportLabelSource = ExportLabelSource.getInstance(field, exportLabel))) {
                exportLabelSourceList.add(exportLabelSource);

                // 处理序号的标题高度
                if (appendSequence) {
                    appendSequence = false; // 断路器，不再处理该判断分支
                    exportLabelSourceList.add(0, ExportLabelSource.getDefaultHeaderAppend(exportLabelSource.getTitles().size()));
                }
            }
        }
        exportLabelSourceList.sort(Comparator.comparingInt(ExportLabelSource::getSequence));
        exportLabelSourceCollection.put(clazz, exportLabelSourceList);

        return exportLabelSourceList;
    }


    /*------------------------------------------------protected methods-----------------------------------------------*/


    /**
     * 判断给定的数据是否需要进行最后的合计项处理
     *
     * @param clazz 给定的数据
     *
     * @return 判断结果
     */
    protected boolean appendTotalRow(Class<?> clazz) {
        return getAnnotationExcelSourceList(clazz).parallelStream().anyMatch(ExportLabelSource::isAggregated);
    }

    /**
     * 获取最终合并后的 Excel 导出标题
     *
     * @param excelTopLabelSource 顶级标题
     * @param titleList           次级标题集合
     *
     * @return 最终合并后的 Excel 导出标题
     */
    protected List<List<String>> mergedTopTitleAndTitleList(@NotNull ExcelTopLabelSource excelTopLabelSource, List<List<String>> titleList) {
        final int size = titleList.size();
        final String topTitle = excelTopLabelSource.getTopTitle();
        final int topTitleHighly = excelTopLabelSource.getHighly();
        final List<List<String>> completelyTitleList = new ArrayList<>(size);

        for (List<String> underTitles : titleList) {
            final List<String> innerTitleList;
            completelyTitleList.add(innerTitleList = new LinkedList<>());

            for (int index = 0; index <= topTitleHighly; index++) {
                if (topTitleHighly != index) {
                    if (StrUtil.isNotEmpty(topTitle)) {
                        innerTitleList.add(topTitle);
                    }
                } else {
                    innerTitleList.addAll(underTitles);
                }
            }
        }
        return completelyTitleList;
    }

    /**
     * 获取给定日期格式化模板对应的 SimpleDateFormat 对象
     *
     * @param excelFormatSource 日期格式化模板
     *
     * @return 给定日期格式化模板对应的 SimpleDateFormat 对象
     */
    protected DateTimeFormatter getSimpleDateFormat(ExcelFormatSource excelFormatSource) {
        DateTimeFormatter dateTimeFormatter;
        String pattern = Objects.nonNull(excelFormatSource) ? excelFormatSource.getFormatPattern() : DateFormat.DEFAULT_DATE_PATTERN;

        if (Objects.isNull(dateTimeFormatter = dateTimeFormatterCollection.get(pattern))) {
            dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
            dateTimeFormatterCollection.put(pattern, dateTimeFormatter);
        }
        return dateTimeFormatter;
    }


    /*----------------------------------------------private static class----------------------------------------------*/


    public static class InnerUtil {

        /**
         * 初始化给定值；如果为空则返回0
         *
         * @param decimal 给定的值
         *
         * @return 初始化后的值
         */
        public static BigDecimal initDecimal(BigDecimal decimal) {
            return Objects.nonNull(decimal) ? decimal : ZERO;
        }

        /**
         * 安全的将给定的两数相加
         *
         * @param addend  加数
         * @param summand 被加数
         *
         * @return 两数之和
         */
        public static BigDecimal securityIncrease(BigDecimal addend, BigDecimal summand) {
            return initDecimal(addend).add(initDecimal(summand));
        }
    }
}
